using UnityEngine;
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

public enum DisType { 
    Exception,
    Disconnect,
}

public enum NetActionType {
    Connect,
    Message,
    Logout,
	None
}

public enum ConnectStatus {
    None,
    Connecting,
    Connected,
    Error,
}

public class SocketClient {
	private enum Session
	{
		SESSION_NULL,
		SESSION_SIGN,
		SESSION_VERIFY,
		SESSION_VERIFY_FAILED,
		SESSION_RECV_KEY,
		SESSION_TRAFFIC,
	}

    private const int MAX_READ = 8192;

    private ConnectStatus status = ConnectStatus.None;
	private Session sessionState = Session.SESSION_NULL;

    private TcpClient client = null;
    private RSACryptoService rsaServer = null;
    private uint sessionid = 0;
    private byte[] readBuffer = new byte[MAX_READ];
    private byte[] verifyData = new byte[8];
    private zreader zReader = null;
    private string errorMsg = "";    
    private List<ByteBuffer> recvMessage = null;

	//服务器的公钥
	private static string java_publickey = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDeyg3TGNnmgDbr0j2gFhGy93WB
m0ciBatzUt7vhZMBdUUYoqDTHK5wtV/dabPDaipGeVkzFdUG6GRL+s++z5F6lUtB
gEr8fidNfcCIfShqmR5L4So76W5cOIPsGoM2igWFJQL4QDjVmML4NsqLc8OaZ9ej
1CurS3aoROcCXoAw5QIDAQAB";
	//客户端的私钥
	private static string java_privatekey = @"MIICXQIBAAKBgQC7SmozcaeKk8Okmd22fmrZjY0i+W/IBdUgWcQzLipYN2TZmd9t
NgcSFBd9dfRcYmaX3Lyjpdm5cKXLr1ctlYIN2+TkL22AnKSi/4SNunT4t+i0Olzs
Zez+QwRUh/EbQAWq9bAOWMqfyswdx5fcGerKvDpxgQs2m4vDxbX/ZFRFRwIDAQAB
AoGAWh1S/g+oal/wmYlDCWTIKocWKobUBuzvgBJQ+cMzsqBskNqdiyGcw1ERgFc5
zR23eUhHJ4JMQRJ3Y4qpKpCuMwQs0jGvWY4Jf+B5mh6RmHVA3Olmv8bHLFh2iJOH
LEgV5/ICyi++2z50tohx3vn3wzWhhYoW1GIb9QJMBdPOJHkCQQDbCZ8cmp2uH61/
8G3rmywICzhpl4BYH7dtHCMg3Z6Hqi5JEs2neneTvmZfGwCY51YL9ms9cssmw2XB
DDwopzotAkEA2uVVb6/Hu068gcAfH+qF/hN0NueGwFXzRmffOhXmYUsUTcSqk932
cCjIlehHGXbkGnUl44CIBo4qOGK9vQDpwwJAY+lhoKSOZEyi0YcUPLJNRWYI13F5
47ij7Ks3AtjUZUGlV0Oyd0CPpt7kx2EDxrtPLqm6hQ8Fx6q9kW9JSanuCQJBAIS6
yNqHUOof7SgUEgttTsBolXBxZYEc3P3VIEN9Ygue1fnuBazRy4vo/u//P5WORPRS
Ep5nopOvAqTcIscHVbcCQQCdMJvncpw3K1cISx3bHrOLmE4Vh0V7CdEp1fjIF+VG
Xp+GtR7mlA6e00gzG7xCuOGi0zis4KzpUKLi2Tpij9d9";



	public SocketClient() 
    {
        rsaServer = new RSACryptoService(java_privatekey, java_publickey);
    }

    // 连接服务器
    public void Connect(string host, int port) 
	{
        if (status != ConnectStatus.None && status != ConnectStatus.Error) {
            Log("can NOT Connect, ConnectStatus MUST in None or Error, current:" + status.ToString());
            return;
        }

        status = ConnectStatus.Connecting;
		sessionState = Session.SESSION_SIGN;
        sessionid++;

        // init client
        client = null;
        client = new TcpClient();
        client.SendTimeout = 1000;
        client.ReceiveTimeout = 1000;
        client.NoDelay = true;
        
        // init zReader
        zReader = null;
        zReader = new zreader ();
        zReader.zreader_init (12);

        // init messageQueue
        recvMessage = null;
        recvMessage = new List<ByteBuffer>();

        try {
            client.BeginConnect(host, port, new AsyncCallback(OnConnect), sessionid);
        } catch (Exception e) {
            // connect失败
            OnError(e.GetType().ToString() + " : " + e.Message);
        }
    }

    // BeginConnect结果回调
    private void OnConnect(IAsyncResult asr)
    {
        bool connSuccess = true;
        try {
            client.EndConnect (asr);
            if ((uint)asr.AsyncState != sessionid || !client.Connected) {
                connSuccess = false;
            }
        } catch (SocketException ex) {
            connSuccess = false;
        }
        if (!connSuccess) {
            OnError("连接超时，请检查您的网络设置");
            return;
        }
        // 启动读流程
        client.GetStream().BeginRead(readBuffer, 0, MAX_READ, new AsyncCallback(OnRead), asr.AsyncState);
    }

    // 异步读回调
    private void OnRead(IAsyncResult asr) 
    {
        int bytesRead = 0;
        try {
            //对不上了，这说明是前一次的数据，这时候TCP CLIENT已经被清掉，数据得丢掉
            if ((uint)asr.AsyncState != sessionid) {
                return;
            }
            lock (client.GetStream()) {         //读取字节流到缓冲区
                bytesRead = client.GetStream().EndRead(asr);
            }
            if (bytesRead < 1) {                //包尺寸有问题，断线处理
                OnDisconnected(DisType.Disconnect, "bytesRead < 1 远程主机关闭了连接！！！");
                return;
            }

			OnReceive(readBuffer, bytesRead);   //分析数据包内容，抛给逻辑层

            lock (client.GetStream()) {         //分析完，再次监听服务器发过来的新消息
                Array.Clear(readBuffer, 0, readBuffer.Length);   //清空数组
                client.GetStream().BeginRead(readBuffer, 0, MAX_READ, new AsyncCallback(OnRead), asr.AsyncState);
            }
        } catch (Exception e) {
            OnDisconnected(DisType.Exception, e.GetType().ToString() + " : " + e.Message);
        }
    }

    // 接收到消息
    private void OnReceive(byte[] bytes, int length)
    {
        zReader.zreader_read (bytes, length);
        int i = 0;
        int start = 0;

        for(i = 0, start = 0; i < zReader.eobcnt; start = zReader.eob[i], ++i)
        {
            int packetlen = zReader.eob[i] - start;
            byte[] data = new byte[packetlen];
            Array.ConstrainedCopy (zReader.buf, start, data, 0, packetlen);
            switch(sessionState)
            {
            case Session.SESSION_SIGN:
                OnSessionSign(data);
                break;
            case Session.SESSION_VERIFY:
                OnSessionVerify(data);
                break;
            case Session.SESSION_RECV_KEY:
                OnSessionRecvKey(data);
                break;
            case Session.SESSION_TRAFFIC:
                // 进入正常的协议传输，这里开始无加密
                OnReceivedMessage(data);
                break;
            case Session.SESSION_VERIFY_FAILED:
                OnSessionVerifyFailed();
                break;
            }
        }
        zReader.zreader_clear ();
    }

    // 握手过程 Sign
	private void OnSessionSign(byte[] bytes)
	{
		if (rsaServer == null)
			return;
		byte[] res = rsaServer.Decrypt (bytes);

		// 将解密出来的数据直接回发给服务器，不加数据长度
		Write (res);

		System.Random r = new System.Random();
		for (int i = 0; i < 8; ++i)
		{
			verifyData[i] = (byte)r.Next(0, 255);
		}
		byte[] change_data = rsaServer.Encrypt (verifyData);
		Write (change_data);
		sessionState = Session.SESSION_VERIFY;
	}

    // 握手过程 Verify
	private void OnSessionVerify(byte[] bytes)
	{
		bool succeed = true;
		for (int i = 0; i < 8; ++i)
		{
			if (verifyData[i] != bytes[i])
			{
				succeed = false;
				break;
			}
		}
		if (succeed) {
			sessionState = Session.SESSION_RECV_KEY;
		} else {
			sessionState = Session.SESSION_VERIFY_FAILED;
		}
	}

    // 握手过程 RecvKey
	private void OnSessionRecvKey(byte[] bytes)
	{
		if (rsaServer == null)
			return;
		byte[] rc4_key_byte = rsaServer.Decrypt (bytes);
		rc4.rc4_key = (int)((rc4_key_byte[0] & 0xFF)
		                    | ((rc4_key_byte[1] & 0xFF) << 8)
		                    | ((rc4_key_byte[2] & 0xFF) << 16)
		                    | ((rc4_key_byte[3] & 0xFF) << 24));

		rc4.CreateContext();
		sessionState = Session.SESSION_TRAFFIC;

        // 连接成功
        status = ConnectStatus.Connected;  
	}

    // 正常的协议传输, 接收消息放入篮子
    private void OnReceivedMessage(byte[] data)
    {
        ByteBuffer buff = new ByteBuffer(data);
		lock (recvMessage) {
			recvMessage.Add(buff);
		}
    }

    // 握手验证失败
    private void OnSessionVerifyFailed()
    {
        OnError("Session Verify Failed");
    }
    
    // 写消息
    public void Write(byte[] message) 
    {
        if (client == null || !client.Connected) {
            Log("Drop message, TcpClient NOT connected");
            return;
        }

        // 如果session在握手中, 必须处于Connecting状态, 如果不在握手中, 必须处于已连接状态
        if (sessionState == Session.SESSION_TRAFFIC) {
            if (status != ConnectStatus.Connected) {
                Log("Drop message, MUST in ConnectStatus.Connected status, current:" + status.ToString());
                return;
            }
        } else {
            if (status != ConnectStatus.Connecting) {
                Log("Drop message, MUST in ConnectStatus.Connecting status, current:" + status.ToString());
                return;
            }
        }

        byte[] rc4_message = message;
        if (sessionState == Session.SESSION_TRAFFIC)
        {
            rc4_message = rc4.RC4_Transform(rc4_message);
        }

        using (MemoryStream ms = new MemoryStream()) {
            ms.Position = 0;
            BinaryWriter writer = new BinaryWriter(ms);
            UInt32 msglen = (UInt32)rc4_message.Length;
            writer.Write(msglen);
            writer.Write(rc4_message);
            writer.Flush();

            byte[] payload = ms.ToArray();
            lock (client.GetStream()) {
                client.GetStream().BeginWrite (payload, 0, payload.Length, new AsyncCallback (OnWrite), sessionid);
            }
        }
    }

    // 异步写回调
    private void OnWrite(IAsyncResult r)
    {
        try {
            if ((uint)r.AsyncState != sessionid) {
                return;
            }
            lock(client.GetStream()){
                client.GetStream().EndWrite(r);
            }
        } catch (Exception e) {
            OnError(e.GetType().ToString() + " : " + e.Message);
        }
    }

    // 丢失链接(被动)
    private void OnDisconnected(DisType dis, string msg)
    {
        OnError(msg);
    }

    // 出错
    private void OnError(string msg)
    {
        Log("[ERROR]" + msg);
        // 连接失败
        status = ConnectStatus.Error;
        errorMsg = msg;
        // 清理连接
        Clear();
    }

    // 断开连接(主动)
    public void Disconnect()
    {
        if (client == null || !client.Connected) {
            Log("can NOT disconnect, client MUST be Connected, current:" + status.ToString());
        } else {
            if (status == ConnectStatus.Connected)
            {
                Log("Disconnect...");
                // 断开连接
                status = ConnectStatus.None;
                Clear();
            }
        }
    }

    // 清理
    private void Clear()
    { 
		sessionState = Session.SESSION_NULL;

        if (client != null) {
            sessionid++;
            if (client.Connected) {
                client.Close();
            }
            client = null;
        }

        if (zReader != null) {
            zReader.zreader_clear();
            zReader.zreader_deinit();
            zReader = null;
        }

        Array.Clear(readBuffer, 0, readBuffer.Length);
        Array.Clear(verifyData, 0, verifyData.Length);
    }

    // 获取收到的网络消息
    public ByteBuffer[] FetchReceivedMessage()
    {
		if (recvMessage != null) 
		{
			lock(recvMessage) {
				if (recvMessage.Count == 0)
					return null;
				
	            ByteBuffer[] res = recvMessage.ToArray();
	            recvMessage.Clear();
				return res;
			}
        } else {
            return null;
        }
    }

    // 是否在连接状态
    public bool Connected() {
        return status == ConnectStatus.Connected;
    }

    public ConnectStatus GetStatus() {
        return status;
    }

    public string GetError() {
        return status == ConnectStatus.Error ? errorMsg : "";
    }

    private void Log(string msg) {
        Debug.Log("[SocketClient]"+msg);
    }













    // 登出
  //   public static void Logout() { 
		// lock (eventLock) {
		// 	_events.Enqueue (new KeyValuePair<NetActionType, readBuffer> (NetActionType.Logout, null));
		// }
  //   }

    // 发送连接请求
  //   public static void SendConnect(string addr, int port) {
  //       //GameManager.Instance.GetComponent<SocketClient>().errorMsg = "";
		// NetworkManager.Instance.SocketClient.errorMsg = "";
  //       readBuffer buffer = new readBuffer();
  //       buffer.WriteString(addr);
  //       buffer.WriteInt(port);
		// lock (eventLock) {
		// 	_events.Enqueue (new KeyValuePair<NetActionType, readBuffer> (NetActionType.Connect, new readBuffer (buffer.ToBytes ())));
		// }
  //       buffer.Close();
  //   }

    // 发送消息
  //   public static void SendMessage(readBuffer buffer) {
		// lock (eventLock) {
		// 	_events.Enqueue (new KeyValuePair<NetActionType, readBuffer> (NetActionType.Message, buffer));
		// }
  //   }

  //   // 获取状态
  //   public static bool ConnectSuccess() {
  //       //return GameManager.Instance.GetComponent<SocketClient>().sessionState == Session.SESSION_TRAFFIC;
		// return NetworkManager.Instance.SocketClient.sessionState == Session.SESSION_TRAFFIC;
  //   }

  //   public static string GetErrorMsg() {
  //       //return GameManager.Instance.GetComponent<SocketClient>().errorMsg;
		// return NetworkManager.Instance.SocketClient.errorMsg;
  //   }
}
